/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation
**
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
**     the names of its contributors may be used to endorse or promote
**     products derived from this software without specific prior written
**     permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
** $QT_END_LICENSE$
**
****************************************************************************/



// System Includes.
#include <e32std.h>
#include <e32base.h>

//! User Includes.
#include "stringrenderer_symbian.h"
#include "descriptorexamples_symbian.h"

/*!
 * -----------------------------------------------------------------------------
 * The example class below and helper functions are used by examples.
 * -----------------------------------------------------------------------------
 */
    class TExample
        {
        public:
            TExample() : iNumber(0), iText() {}
            TExample(TInt aNumber, const TDesC &aText);
        public:
            TInt iNumber;
            TBuf<16> iText;
        };

    // render message and characteristics of TExample class
    // e.g. 'message: 10, "abcd"'
    LOCAL_C void ShowContents(const TDesC &aMsg,
                              const TExample aExample,
                              TPtr &aOutput );

    // render message and contents of given descriptor character data,
    // e.g. 'message: "abcd"'
    LOCAL_C void ShowContents(const TDesC &aMsg,
                              const TDesC &aTxt,
                              TPtr &aOutput);

    // render message and contents of given descriptor character data,
    // e.g. 'message: "abcd"'
    LOCAL_C void ShowContents(const TDesC &aMsg,
                              const TDesC8 &aTxt,
                              TPtr &aOutput);

    // render number of items in fifo and maximum number of
    // items the given fifo can store. e.g. 'fifo: size=1, max=5'.
    template <class T>
    LOCAL_C void ShowContents(CCirBuf<T> *aFIFO, TPtr &aOutput);

    // Render characteristics of flat dynamic buffer storing 8 bit characters.
    // The result is similar to this example:
    //
    //   CBufFlat: size=8, data @13984184=
    //   "abcdefgh"
    //
    LOCAL_C void ShowBuf(CBufFlat &aBuf, TDes &aOutput);

    // Render characteristics of segmented dynamic buffer storing 8 bit
    // characters. The result is similar to this example:
    //
    // CBufSeg: size=28, segments=
    //   "abcdefghij" @13984212
    //   "klmnopqrst" @13984248
    //   "uvwxyz01" @13984284
    //
    LOCAL_C void ShowBuf(CBufSeg &aBuf, TDes &aOutput);

    // Render characteristics of package buffer storing TExample object.
    // The result is similar to this example:
    //
    //  TPckgBuf @309000944, sizeof=52, storing
    //    TExample @309000952, sizeof=44, iNumber=1234567, iText="Hello!"
    //
    LOCAL_C void ShowContents(TPckgBuf<TExample> &aPackageBuf, TPtr &aOutput);

    // Render characteristics of package buffer pointer referring to TExample
    // object. The result is similar to this example:
    //
    //  TPckg @309000932, sizeof=12, referring to
    //    TExample @309001012, sizeof=44, iNumber=12345, iText="Hello!"
    //
    LOCAL_C void ShowContents(TPckg<TExample> &aPackage, TPtr &aOutput);

// Texts for adding and removing operations
_LIT( KAddedMsg, "\nAdded" );
_LIT( KRemovedMsg, "\nRemoved" );
_LIT8( KChars8, "abcdefghijklmnopqrstuvwxyz0123456789" );

/*!
 * -----------------------------------------------------------------------------
 * This example method is documented in header file "DescriptorExamples.h"
 * -----------------------------------------------------------------------------
 */
void CDescriptorExamples::CircularBuffersL()
    {
    TPtr output( iViewer->GetViewBuffer() );
    _LIT(KCircularBuffersTxt, "CircularBuffers:TText");
    RenderHeader( KCircularBuffersTxt, output );

    // -------------------------------------------------------------------------
    // This first example demonstrates how circular buffers can be used to
    // buffer character data.
    TBuf<10> deletedChars; // stores characters removed from fifo
    _LIT( KNumbersTxt, "0123456789" );
    // point to first character in character array stored in TLitC
    TText *charPtr = (TText*)KNumbersTxt().Ptr();
    CCirBuf<TText> *textFIFO;

    // construct fifo able to store up to ten characters:
    // contents of fifo is shown below (H=head, T=Tail).
    // fifo: data= "__________", count = 0
    //              ^
    //             H&T
    textFIFO = new (ELeave) CCirBuf<TText>();
    CleanupStack::PushL( textFIFO );
    textFIFO->SetLengthL( 10 ); // can store up to ten characters

    // Copy first 5 items (characters) to fifo
    // fifo: data= "01234_____", count = 5
    //              ^    ^
    //              T    H
    textFIFO->Add( charPtr, 5 ); // 01234
    ShowContents( KAddedMsg, KNumbersTxt().Left(5), output );
    ShowContents( textFIFO, output ); // size=5, max=10

    // remove three items from fifo (first three characters) and
    // store copies of them to deletedChars buffer.
    // fifo: data= "___34_____", count = 2
    //                 ^ ^
    //                 T H
    textFIFO->Remove( (TText*)deletedChars.Ptr(), 3 ); // chars "012"
    deletedChars.SetLength(3);
    ShowContents( KRemovedMsg, deletedChars, output );
    ShowContents( textFIFO, output ); // size=5, max=10

    // add 6 characters so the head rounds to the beginning.
    // fifo: data= "5__3401234", count = 8
    //               ^ ^
    //               H T
    textFIFO->Add( charPtr, 6 ); // 012345
    ShowContents( KAddedMsg, KNumbersTxt().Left(6), output );
    ShowContents( textFIFO, output ); // size=8, max=10

    // try to add 4 characters. Since there isn't enough space
    // only two characters are added since no more room available.
    // fifo: data= "5013401234", count = 10
    //                 ^
    //                H&T
    TInt charsAdded = textFIFO->Add( charPtr, 4 ); // 0123
    ShowContents( KAddedMsg, KNumbersTxt().Left(4), output );
    _LIT(KMsg, "But only %d characters was really added\n");
    output.AppendFormat( KMsg, charsAdded );
    ShowContents( textFIFO, output ); // size=10, max=10

    // remove eight items from fifo (now tail rounds to beginning)
    // fifo: data= "_01_______", count = 2
    //               ^ ^
    //               T H
    textFIFO->Remove( (TText*)deletedChars.Ptr(), 8 ); // chars "34012345"
    deletedChars.SetLength(8);
    ShowContents( KRemovedMsg, deletedChars, output );
    ShowContents( textFIFO, output ); // size=2, max=10

    // -------------------------------------------------------------------------
    // This example introduces how CCirBuf is used to store complex data types.
    // Since CCirBuf is templated its items can be any type.
    _LIT(KCircularBuffersEx, "CircularBuffers:TExample");
    RenderHeader( KCircularBuffersEx, output );

    // declare pointer to circular buffer (FIFO) that is able
    // to store TExample items.
    CCirBuf<TExample> *exampleFIFO;
    // when items are removed one by one, the copy of deleted item
    // is copied to this instance
    TExample removedItem;

    // Construct an instance of FIFO that can store up to
    // five Example class instances.
    exampleFIFO = new (ELeave) CCirBuf<TExample>();
    CleanupStack::PushL( exampleFIFO );
    exampleFIFO->SetLengthL( 5 );

    // Populate FIFO with three items and show contents
    // of added item and state of FIFO.
    _LIT(KOne, "one");
    TExample one(1, KOne);
    exampleFIFO->Add( &one );
    ShowContents( KAddedMsg, one, output );
    ShowContents( exampleFIFO, output ); // size=1, max=5

    _LIT(KTwo, "two");
    TExample two(2, KTwo);
    exampleFIFO->Add( &two );
    ShowContents( KAddedMsg, two, output );
    ShowContents( exampleFIFO, output ); // size=2, max=5

    _LIT(KThree, "three");
    TExample three(3, KThree);
    exampleFIFO->Add( &three );
    ShowContents( KAddedMsg, three, output );
    ShowContents( exampleFIFO, output ); // size=3, max=5

    // Remove item by item from FIFO and show contents of
    // removed item and the state of FIFO.
    // Remove() method takes a pointer to object where contents
    // of removed item is copied (binary copy)
    while( exampleFIFO->Count() > 0 )
        {
        exampleFIFO->Remove( &removedItem );
        ShowContents( KRemovedMsg, removedItem, output );
        ShowContents( exampleFIFO, output );
        }

    iViewer->UpdateView();
    CleanupStack::PopAndDestroy(2); // exampleFIFO, textFIFO
    }
/*!
 * -----------------------------------------------------------------------------
 * This example method is documented in header file "DescriptorExamples.h" and
 * implementation specific documentation can be read below.
 * -----------------------------------------------------------------------------
 */
void CDescriptorExamples::FlatDynamicBuffersL()
    {
    TPtr output( iViewer->GetViewBuffer() );
    _LIT(KFlatDynamicBuffers, "FlatDynamicBuffers");
    RenderHeader( KFlatDynamicBuffers, output );

    // -------------------------------------------------------------------------
    // When content (8 bit characters) are added to container, it allocates the
    // memory automatically. In the first steps it can extend the existing heap
    // cell when more space is needed.
    //
    // However, later in the example a HBufC is allocated from heap to prevent
    // heap cell extending. When more data is appended to buffer, a new heap
    // cell is allocated from somewhere else, existing content is copied to new
    // cell, new content to be appended is added after it and finally the
    // existing heap cell is deallocated.
    //
    // The contents of buffer is shown after each character insertion. The
    // location of data (memory address of heap cell) doesn't change until the
    // fourth insertion because heap cell can't extend anymore.

    // construct a flat dynamic buffer that will automatically extend its heap
    // cell size with 10 bytes when more space is needed.
    CBufFlat *buf = CBufFlat::NewL( 10 );
    CleanupStack::PushL(buf);

    // this variable is used to refer a portion of characters in KChars8
    TPtrC8 charsToInsert(NULL, 0);

    charsToInsert.Set( KChars8().Mid( 0, 8 ) ); // "abcdefgh"
    buf->InsertL( 0, charsToInsert );
    ShowContents( KAddedMsg, charsToInsert, output );
    ShowBuf( *buf, output );

    charsToInsert.Set( KChars8().Mid( 8, 8 ) ); // "ijklmnop"
    buf->InsertL( 8, charsToInsert );
    ShowContents( KAddedMsg, charsToInsert, output );
    ShowBuf( *buf, output );

    charsToInsert.Set( KChars8().Mid( 16, 12 ) ); // "ijklmnop"
    buf->InsertL( 16, charsToInsert );
    ShowContents( KAddedMsg, charsToInsert, output );
    ShowBuf( *buf, output );

    // this object is allocated just after the flat dynamic buffers heap
    // cell preventing it to extend its cell size when more space is needed.
    HBufC *tmpBuf = HBufC::NewL( 50 );
    CleanupStack::PushL( tmpBuf );
    tmpBuf->Length(); // remove compiler warning that variable is not used

    // appending this data to buffer requires heap cell to extend.
    // tmpBuf prevents heap cell to extend so new cell has to be
    // allocated, original data copied to there and existing cell
    // to be freed.
    charsToInsert.Set( KChars8().Mid( 4, 20 ) ); // "efghijklmnopqrstuvwx"
    buf->InsertL(28, charsToInsert );
    ShowContents( KAddedMsg, charsToInsert, output );
    ShowBuf( *buf, output );

    iViewer->UpdateView();
    CleanupStack::PopAndDestroy(2); // tmpBuf, buf
    }
/*!
 * -----------------------------------------------------------------------------
 * This example method is documented in header file "DescriptorExamples.h"
 * -----------------------------------------------------------------------------
 */
void CDescriptorExamples::SegmentedDynamicBuffersL()
    {
    TPtr output( iViewer->GetViewBuffer() );
    _LIT(KSegmentedDynamicBuffers, "SegmentedDynamicBuffers");
    RenderHeader( KSegmentedDynamicBuffers, output );

    // Allocate a empty buffer with granularity of 10
    CBufSeg* buf = CBufSeg::NewL(10);
    CleanupStack::PushL( buf );

    TPtrC8 charsToInsert(NULL, 0);

    // append "abcdefgh"
    // All eight characters fit to first segment
    charsToInsert.Set( KChars8().Mid(0, 8) );
    buf->InsertL(0, charsToInsert );
    ShowContents( KAddedMsg, charsToInsert, output );
    ShowBuf( *buf, output );

    // append "ijklmnop"
    // first two characters go to the first segment and
    // the rest six to second one.
    charsToInsert.Set( KChars8().Mid(8, 8) );
    buf->InsertL(8, charsToInsert );
    ShowContents( KAddedMsg, charsToInsert, output );
    ShowBuf( *buf, output );

    // append "qrstuvwxyz01"
    // first four characters go to the second segment and
    // the rest eight to third one.
    charsToInsert.Set( KChars8().Mid(16, 12) );
    buf->InsertL(16, charsToInsert );
    ShowContents( KAddedMsg, charsToInsert, output );
    ShowBuf( *buf, output );

    // append "efghijklmnopqrstuvwx"
    // first two characters go to the third segment. Next ten to
    // fourth segment and rest eight to fifth segment.
    charsToInsert.Set( KChars8().Mid(4, 20) );
    buf->InsertL(28, charsToInsert );
    ShowContents( KAddedMsg, charsToInsert, output );
    ShowBuf( *buf, output );

    // delete a portion from the segmented buffer.
    //
    // An exmaple run shows that segments before deletion are
    //
    // CBufSeg: size=48, segments=
    //   "abcdefghij" @13984212
    //   "klmnopqrst" @13984248
    //   "uvwxyz01ef" @13984284
    //   "ghijklmnop" @13984356
    //   "qrstuvwx" @13984320
    //
    // and after deletion there are segments:
    //
    // CBufSeg: size=24, segments=
    //   "abc" @13984212
    //   "1ef" @13984284
    //   "ghijklmnop" @13984356
    //   "qrstuvwx" @13984320
    //
    // From this example it can be seen that modifying segment
    // buffer can result segments not fully filled. That's why
    // direct access thought pointer is difficult since the
    // segment sizes varies.
    buf->Delete(3, 24);
    _LIT(KMsg1, "\nDeleted 24 bytes from index 4\n");
    output.Append( KMsg1 );
    ShowBuf( *buf, output );

    // Replace character at even index with 'X'
    //
    // Since data is splitted to segments that may contain variable
    // number of character data it would be quite difficult to alter
    // data with pointer access. That's why the easiest way to alter
    // data is to copy it to fixed sized buffer, modify the copy and
    // write modified content over original data.
    //
    // Since the segmented buffer could be quite long in real problem
    // it is better to get used to modify the content in pieces.
    //
    // Example run produces:
    //
    // CBufSeg: size=24, segments=
    //   "aXc" @13984212
    //   "XeX" @13984284
    //   "gXiXkXmXoX" @13984356
    //   "qXsXuXwX" @13984320
    //
    TBuf8<20> tmpBuf;
    for( TInt i=0; i < buf->Size(); i += tmpBuf.MaxLength() )
    {
        TInt charsToCopy = buf->Size() - i; // Chars left

        // do not try to read more than tmpBuf can hold
        if( charsToCopy > tmpBuf.MaxLength() )
            {
            charsToCopy = tmpBuf.MaxLength();
            }

        // copy data from segmented buffer to descriptor
        buf->Read(i, tmpBuf, charsToCopy);

        // Change character in descriptor at even index
        for(TInt j=0; j<charsToCopy; j++)
            {
            if( j % 2 != 0 )
                {
                    tmpBuf[j] = 'X';
                }
            }

        // write modified content to the same location it was read
        buf->Write(i, tmpBuf, charsToCopy);
    }
    _LIT(KMsg2, "\nReplaced characters at even index with 'X'\n");
    output.Append( KMsg2 );
    ShowBuf( *buf, output );

    // lets compress the data to minimize heap usage: partially
    // filled segments are fullfilled and segments no more
    // needed are deallocated.
    //
    // Example run produces:
    //
    // CBufSeg: size=24, segments=
    //   "aXcXeXgXiX" @13984212
    //   "kXmXoXqXsX" @13984356
    //   "uXwX" @13984320
    //
    // (first and second segments were fullfilled. There didn't
    //  exist any data in fourth segment so if was deleted)
    //
    buf->Compress();
    _LIT(KCompressedBuf, "\nCompressed buffer\n");
    output.Append( KCompressedBuf );
    ShowBuf( *buf, output );

    iViewer->UpdateView();
    CleanupStack::PopAndDestroy(1);
    }

/*!
 * -----------------------------------------------------------------------------
 * This example method is documented in header file "DescriptorExamples.h"
 * -----------------------------------------------------------------------------
 */
void CDescriptorExamples::PackageBuffers()
    {
    TPtr output( iViewer->GetViewBuffer() );
    // ------------------------------------------------------------
    // This first example demonstrates how package buffer is used to
    // pack a value class inside.
    _LIT(KPackageBuffers1, "PackageBuffers:TPckgBuf");
    RenderHeader( KPackageBuffers1, output );

    // construct package buffer that does store one instance of TExample
    // instance. The insnce inside is empty since constructor of package
    // buffer does call also the constructor of capsulated object
    // (constructor of TExample in that case that sets fields to empty).
    TPckgBuf<TExample> pckgBuf;
    _LIT(KMsg3, "nCreated package buffer that stores an empty (by default) TExample object\n");
    output.Append( KMsg3 );

    ShowContents( pckgBuf, output );

    // modify TExample inside package buffer.
    //
    // To get access to the instance, operator () is used to get
    // reference.
    TExample &exampleRef = pckgBuf();
    exampleRef.iNumber = 1234567;
    _LIT(KMsg4, "\nModified iNumber of TExample inside package buffer\n");
    output.Append( KMsg4 );
    ShowContents( pckgBuf, output );

    // modify TExample inside package buffer - using existing reference
    _LIT(KHello, "Hello!");
    exampleRef.iText.Copy( KHello );
    _LIT(KMsg5, "\nModified iText of TExample inside package buffer\n");
    output.Append( KMsg5 );
    ShowContents( pckgBuf, output );

    // ------------------------------------------------------------
    // This second example demonstrates how package buffer pointer is
    // used to refer contenst of value class somewhere in memory.
    _LIT(KPackageBuffers2, "PackageBuffers:TPckg");
    RenderHeader( KPackageBuffers2, output );

    TExample example;
    TPckg<TExample> pckg( example );
    _LIT(KMsg6, "\nCreated package buffer that refers to an empty TExample object\n");
    output.Append( KMsg6 );
    ShowContents(pckg, output);

    // modify contents the example object. Showing contents of package
    // buffer reveals that package buffer did refer to our external
    // instance.
    example.iNumber = 12345;
    example.iText.Copy( KHello );
    _LIT(KMsg7, "\nCreated package buffer that refers to an empty TExample object\n");
    output.Append( KMsg7 );
    ShowContents(pckg, output);

    iViewer->UpdateView();
    }

void CDescriptorExamples::RBufDemonstrations()
    {
    // RBuf::CreateL demo
    RBuf modifiableBuf;
    modifiableBuf.CreateL(12);
    ASSERT(modifiableBuf.Length()==0);
    ASSERT(modifiableBuf.MaxLength()==12);
    modifiableBuf.Close();

    // RBuf::CreateMaxL  demo
    modifiableBuf.CreateMaxL(12);
    ASSERT(modifiableBuf.Length()==12);
    ASSERT(modifiableBuf.MaxLength()==12);
    modifiableBuf.Close();

    // RBuf::CreateL passing in a descriptor
    _LIT(KHelloWorld, "Hello World");
    modifiableBuf.CreateL(KHelloWorld());
    ASSERT(modifiableBuf.Length()==11);
    ASSERT(modifiableBuf.MaxLength()==11);
    modifiableBuf.Close();

    // RBuf::CreateL passing in a descriptor and a maximum length
    modifiableBuf.CreateL(KHelloWorld(), 15);
    ASSERT(modifiableBuf.Length()==11);
    ASSERT(modifiableBuf.MaxLength()==15);
    modifiableBuf.Close();

    // RBuf::CreateL and ReAllocL & modifiable descriptor base class methods
    _LIT(KHello, "Hello");
    _LIT(KWorld, " World");
    modifiableBuf.CreateL(5);
    modifiableBuf.Copy(KHello());
    modifiableBuf.CleanupClosePushL(); // Push onto cleanup stack for leave safety
    modifiableBuf.ReAllocL(11);
    modifiableBuf.Append(KWorld);
    CleanupStack::PopAndDestroy(); // Calls modifiableBuf.Close()

    //RBuf::Assign leaks easily.
    //If you have memory allocated in the RBuf and you call Assign it leaks.
    //You should always call Close() before calling Assign()
    //The same goes with Create functions, if there's memory allocated it leaks.
    //modifiableBuf.CreateL(1);    
    //modifiableBuf.Close();
    HBufC* hBuf = KHello().AllocL();
    modifiableBuf.Assign(hBuf); //Take ownership of hBuf memory
    ASSERT(modifiableBuf.Length()==5);
    modifiableBuf.Close();

    //Assignments demo
    RBuf myRBuf1;
    RBuf myRBuf2;
    HBufC* myHBufC = HBufC::NewL(20);
    myRBuf1.Assign(myHBufC); //Take ownership of heap memory
    myRBuf2.Assign(myRBuf1); //Take ownership of another RBuf
    myRBuf2.Close();
    
    //Assign, ReAllocL and Append
    TUint16* ptr = static_cast<TUint16*> (User::AllocL(5*sizeof(TText)));    
    modifiableBuf.Assign(ptr,5);
    ASSERT(modifiableBuf.Length()==0);
    modifiableBuf.Copy(KHello()); //Copying any more would panic
    //modifiableBuf.Append(KWorld);//This  would cause a panic

    modifiableBuf.CleanupClosePushL(); //Push onto cleanup stack for leave safety
    modifiableBuf.ReAllocL(12);
    modifiableBuf.Append(KWorld);
    CleanupStack::PopAndDestroy(); //Calls modifiableBuf.Close() 
    }

/*!
 * -----------------------------------------------------------------------------
 * Implementation of the example class
 * -----------------------------------------------------------------------------
 */
TExample::TExample(TInt aNumber, const TDesC &aText)
    {
        iNumber = aNumber;
        iText.Copy(aText.Left( iText.MaxLength() ) );
    }

/*!
 * -----------------------------------------------------------------------------
 * Implementation of the helper functions
 * -----------------------------------------------------------------------------
 */
LOCAL_C void ShowContents(const TDesC& aMsg,
                          const TExample aExample,
                          TPtr &aOutput)
    {
    _LIT( KFormat, "%S: %d, \"%S\"\n" );
    aOutput.AppendFormat( KFormat, &aMsg, aExample.iNumber, &aExample.iText );
    }

LOCAL_C void ShowContents(const TDesC& aMsg, const TDesC &aTxt, TPtr &aOutput)
    {
    _LIT( KFormat, "%S: \"%S\"\n" );
    aOutput.AppendFormat( KFormat, &aMsg, &aTxt );
    }

LOCAL_C void ShowContents(const TDesC& aMsg, const TDesC8 &aTxt, TPtr &aOutput)
    {
    TBuf<128> buf;
    buf.Copy(aTxt.Left(buf.MaxLength()));
    _LIT( KFormat, "%S: \"%S\"\n" );
    aOutput.AppendFormat( KFormat, &aMsg, &buf );
    }

template <class T>
LOCAL_C void ShowContents(CCirBuf<T> *aFIFO, TPtr &aOutput)
    {
    _LIT( KFIFOFormat, "fifo: size=%d, max=%d\n" );
    aOutput.AppendFormat( KFIFOFormat, aFIFO->Count(), aFIFO->Length() );
    }

LOCAL_C void ShowBuf(CBufFlat &aBuf, TDes &aOutput)
    {
    TPtrC8 data = aBuf.Ptr(0);
    _LIT(KFormatMsg, "CBufFlat: size=%d, data @%d=\n\"");
    aOutput.AppendFormat( KFormatMsg, aBuf.Size(), data.Ptr() );
    Append(data, aOutput);
    _LIT(KFormat, "\"\n");
    aOutput.Append(KFormat);
    }

LOCAL_C void ShowBuf(CBufSeg &aBuf, TDes &aOutput)
    {
    _LIT(KFormatMsg, "CBufSeg: size=%d, segments=\n");
    aOutput.AppendFormat( KFormatMsg, aBuf.Size() );
    TInt pos = 0;
    _LIT(KFormat1, "  \"");
    _LIT(KFormat2, "\" @%d\n");
    while( pos < aBuf.Size() )
        {
        TPtrC8 ptr = aBuf.Ptr(pos);
        aOutput.Append( KFormat1 );
        Append(ptr, aOutput);
        aOutput.AppendFormat( KFormat2, ptr.Ptr() );
        pos += ptr.Length();
        }
    }

LOCAL_C void ShowContents(TPckgBuf<TExample> &aPackageBuf, TPtr &aOutput)
    {
    _LIT(KFormat, "TPckgBuf @%d, sizeof=%d, storing\n  TExample @%d, sizeof=%d, iNumber=%d, iText=\"%S\"\n");
    aOutput.AppendFormat( KFormat, &aPackageBuf, sizeof(aPackageBuf), &aPackageBuf(), sizeof(aPackageBuf()), aPackageBuf().iNumber, &aPackageBuf().iText );
    }

LOCAL_C void ShowContents(TPckg<TExample> &aPackage, TPtr &aOutput)
    {
    _LIT(KFormat, "TPckg @%d, sizeof=%d, referring to\n  TExample @%d, sizeof=%d, iNumber=%d, iText=\"%S\"\n");
    aOutput.AppendFormat( KFormat, &aPackage, sizeof(aPackage), &aPackage(), sizeof(aPackage()), aPackage().iNumber, &aPackage().iText );
    }

